///////////////////////////////////////////////////////////////////////////// 
//
//  dwc_e12mp_phy_x4_ns_force_freq_tune.v
//
//  UP16-specific model, calculates and force ideal frequency tune code
//
//  Original Author: Peter Bergstra
//  Current Owner:   Peter Bergstra
//
////////////////////////////////////////////////////////////////////////////// 
//
// Copyright (C) 2013 Synopsys, Inc.  All rights reserved.
//
// SYNOPSYS CONFIDENTIAL - This is an unpublished, proprietary work of
// Synopsys, Inc., and is fully protected under copyright and trade secret
// laws.  You may not view, use, disclose, copy, or distribute this file or
// any information contained herein except pursuant to a valid written
// license agreement. It may not be used, reproduced, or disclosed to others
// except in accordance with the terms and conditions of that agreement.
//
////////////////////////////////////////////////////////////////////////////// 
//
//    Perforce Information
//    $Author: prakav $
//    $File: //dwh/up16/main/dev/pcs_raw/tb/dwc_e12mp_phy_x4_ns_force_freq_tune.v $
//    $DateTime: 2019/03/21 00:12:33 $
//    $Revision: #21 $
//
////////////////////////////////////////////////////////////////////////////// 
`ifdef DWC_E12MP_X4NS_PMA_AFTER_0_1
`include "dwc_e12mp_phy_x4_ns_jtag_id_code.v"
`else
`include "dwc_e12mp_phy_x4_ns_jtag_id_code.v"
`endif
`timescale 1ns / 10fs

module dwc_e12mp_phy_x4_ns_force_freq_tune(
    `ifdef DWC_E12MP_X4NS_PMA_FREQBAND
    input [1:0] rx_cdr_vco_freqband,
    `endif
    input ref_ana_clk,
    input phy_reset,
    input ref_ana_clk_en,
    input ref_ana_div2_en,
    input [12:0] rx_vco_ld_val,
    input rx_cdr_vco_lowfreq,
     `ifdef DWC_C10_PMA
    input [6:0] rx_ref_ld_val,
    inout rx_ana_cdr_freq_tune_clk,
    `else
       input [5:0] rx_ref_ld_val,
    `endif
    inout [9:0] rx_ana_cdr_freq_tune // this port can really be any of input/output/inout since we are forcing it (vcs doesn't complain anyways - not sure about other simulators)
     
);

`ifdef DWC_C8_PMA
`ifdef DWC_PROC56
localparam FREQ_START = (1.7+1.7*13.6/17)*1.0e9; //from analog sim curve, unit: Hz
localparam FREQ_CODE_START = 10'd100; //from analog sim curve
localparam FREQ_END = (6+1.2*1.6/17)*1.0e9; //from analog sim curve
localparam FREQ_CODE_END = 10'd900; //from analog sim curve
`elsif DWC_PROC45
localparam FREQ_START = (1.7+1.7*13.6/17)*1.0e9; //from analog sim curve, unit: Hz
localparam FREQ_CODE_START = 10'd100; //from analog sim curve
localparam FREQ_END = (6+1.2*1.6/17)*1.0e9; //from analog sim curve
localparam FREQ_CODE_END = 10'd900; //from analog sim curve
`elsif DWC_PROC63
localparam FREQ_START = (1.7+1.7*13.6/17)*1.0e9; //from analog sim curve, unit: Hz
localparam FREQ_CODE_START = 10'd100; //from analog sim curve
localparam FREQ_END = (6+1.2*1.6/17)*1.0e9; //from analog sim curve
localparam FREQ_CODE_END = 10'd900; //from analog sim curve
`else
localparam FREQ_START = (1.2+1.2*13.6/17)*1.0e9; //from analog sim curve, unit: Hz
localparam FREQ_CODE_START = 10'd100; //from analog sim curve
localparam FREQ_END = (6+1.2*1.6/17)*1.0e9; //from analog sim curve
localparam FREQ_CODE_END = 10'd900; //from analog sim curve
`endif
`elsif DWC_C10_PMA
localparam FREQ_START = (3.544118)*1.0e9; //from analog sim curve, unit: Hz
localparam FREQ_CODE_START = 10'd100; //from analog sim curve
localparam FREQ_END = (10+2.5*1.6/17)*1.0e9; //from analog sim curve
localparam FREQ_CODE_END = 10'd800; //from analog sim curve
`elsif DWC_PROC16 
localparam FREQ_START = (3.544118)*1.0e9; //from analog sim curve, unit: Hz
localparam FREQ_CODE_START = 10'd100; //from analog sim curve
localparam FREQ_END = (10+2.5*1.6/17)*1.0e9; //from analog sim curve
localparam FREQ_CODE_END = 10'd800; //from analog sim curve
`else
localparam FREQ_START = (2.5+2.5*13.6/17)*1.0e9; //from analog sim curve, unit: Hz
localparam FREQ_CODE_START = 10'd200; //from analog sim curve
localparam FREQ_END = (10+2.5*1.6/17)*1.0e9; //from analog sim curve
localparam FREQ_CODE_END = 10'd800; //from analog sim curve
`endif

localparam SLOPE_FREQ2CODE_REAL = (FREQ_END - FREQ_START) / (FREQ_CODE_END - FREQ_CODE_START); //Unit:Hz/LSB

realtime refclk_period;
realtime refclk_time;
realtime refclk_tmp;
reg [9:0] cnt;
real rx_vco_freq;
reg [9:0] rx_freq_code;
reg [9:0] rx_freq_code_prev;

initial begin
    cnt = 20;
    refclk_time = 0;
    refclk_period = 0;
    rx_vco_freq = 0;
    rx_freq_code = 10'd0;
    refclk_tmp = 0;
end

   // Wait for the ref_clk period to stabilize
   // Stop updating the refclk_period after the cnt has been reached

    always @(posedge ref_ana_clk or posedge phy_reset) begin
      if(phy_reset) 
	cnt <= 10'd20;
      else begin
        if( (cnt > 0)&&(refclk_time > 0.0) ) begin
            cnt <= cnt - 1;
        end
      end
    end

    always @(posedge ref_ana_clk) begin
        refclk_tmp = $realtime;
        if(ref_ana_clk_en && (!phy_reset) )begin

            if( (cnt > 0)&&(refclk_time > 0.0) ) begin
                refclk_period = (refclk_tmp - refclk_time) * 1.0e-9 * (1 + ref_ana_div2_en);
            end

            if(refclk_period > 0.0) begin
	  `ifdef DWC_C10_PMA
                if      (rx_ref_ld_val[6:0]  === 7'd0 ) rx_vco_freq = 1/(refclk_period * 10);
	  `else
                if      (rx_ref_ld_val[5:0]  === 6'd0 ) rx_vco_freq = 1/(refclk_period * 10);
           `endif
                else if (rx_vco_ld_val[12:0] === 13'd0) rx_vco_freq = 1/(refclk_period * 10);
                else                                    rx_vco_freq = rx_vco_ld_val/(refclk_period * rx_ref_ld_val);

	    `ifndef DWC_C8_PMA
            `ifndef DWC_PROC16
            `ifndef DWC_PROC14
            `ifndef DWC_PROC31
            `ifndef DWC_E12MP_UMC28HPCP_X4NS
            `ifndef DWC_PROC36
       		 if(rx_cdr_vco_lowfreq) rx_vco_freq = rx_vco_freq + 1.0e9;
            `endif
            `endif
            `endif
            `endif
            `endif
            `endif

            `ifdef DWC_E12MP_X4NS_PMA_FREQBAND
                 if(rx_cdr_vco_freqband[0] == 1'b0) begin
                   rx_vco_freq = rx_vco_freq + 1.0e9;
                 end else begin
                   rx_vco_freq = rx_vco_freq;
		 end
            `endif

				rx_freq_code_prev=rx_ana_cdr_freq_tune;//store the previous rx_ana_cdr_freq_tune
                if (rx_vco_freq < FREQ_START) rx_vco_freq = FREQ_START; // safety
                if (rx_vco_freq > FREQ_END)   rx_vco_freq = FREQ_END; // safety
                rx_freq_code = FREQ_CODE_START + (rx_vco_freq - FREQ_START) / SLOPE_FREQ2CODE_REAL;


                force rx_ana_cdr_freq_tune = rx_freq_code;

            end
            
            #0.0001;
            refclk_time = refclk_tmp;
        end
        else begin
            rx_vco_freq = FREQ_START;
            refclk_time = 0.0;
        end
    end

 `ifdef DWC_C10_PMA   
always@(rx_freq_code) begin
    if(rx_freq_code_prev != rx_freq_code) begin
        // Force a pulse on the rx_ana_cdr_freq_tune_clk
        force rx_ana_cdr_freq_tune_clk = 0;
        #66.67;
        force rx_ana_cdr_freq_tune_clk = 1;
        #66.67;
        force rx_ana_cdr_freq_tune_clk = 0;
        #66.67;
        release rx_ana_cdr_freq_tune_clk;
    end
end
`endif
    
endmodule
